ICTSC2018 本戦 問題解説: k8s上で立てたPodから名前解決できない

問題文

今最もクールでエキサイティングなコンテナオーケストレーターであるKubernetesを自社でも導入を行う予定で、現在構築を行っている。

クラウド上でのマネージドサービスの前段階として、自社マシンにてKubernetesを導入してみたが、なぜか正常に動作していないようである。 その理由を判断し、Kubernetesクラスタが正常に動くように修正を行ってほしい。

具体的にはコンテナ内からの名前解決が行えていないようである。この問題を解決し、どのPodを起動しても正しくコンテナ内から名前解決できるようにしてほしい。

トラブルの概要

本問題では、Kubernetesクラスタにおいて新しく起動したPodから名前解決が行えないトラブルでした。
実際に下記のようなコマンドを入力した場合にも、digコマンドがタイムアウトエラーが発生してしまう状態にありました。

$ kubectl run dig --image=whywaita/bionic-dnsutils
$ kubectl exec -it $(Podの名前) dig icttoracon.net

→タイムアウト

解説

この問題は、KubernetesのDNS情報を管理するコンポーネントの kube-dns の古い仕様によって、一部環境でのみ発生するバグです。

元ネタのIssueはこちらです。

近年のLinuxディストリビューションにおいて、systemdの採用が進んでおり、特にUbuntuにおいては多くのsystemdコンポーネントを採用しています。

Ubuntu 18.04にて採用されたnetplanなどは記憶に新しいでしょうか。netplanはYAML形式の設定ファイルを用いてLinuxのネットワーク設定を行えるようになったものですが、動作としてはsystemd-networkdというバックエンドのコンポーネント向けの設定ファイルを生成し、ネットワーク設定を動作させています。

1

systemd-networkd とともに採用されているコンポーネントが systemd-resolved です。詳細な説明は割愛しますが、DNSキャッシュサーバを各サーバ内で起動させておくデーモンです。

実際に動作しているUbuntuで設定を確認してみましょう。Ubuntu (やその他多くのLinuxディストリビューション) においてDNSサーバの設定は /etc/resolv.conf に記載されています。以前のディストリビューションであれば以下のようにDNSサーバを指定していました。

$ cat /etc/resolv.conf
nameserver 8.8.8.8 # Google Public DNS
nameserver 8.8.4.4

ですが、systemd-resolvedがインストールされている場合は以下のように指定されています。

$ cat /etc/resolv.conf
nameserver 127.0.0.53

127.0.0.53 はloopbackアドレスであり自分自身を指します。systemd-resolvedが動いていることで自分自身に対して名前解決を行う事で高速にクエリ返答が行えるようになったということになります。

さて、今回問題が発生したコンテナにおいても、名前解決を行う場合にはDNSサーバの指定が必要です。問題が起きたバージョンにおいて、kube-dnsが参照するDNSサーバの設定はどこから取得するのでしょうか。

答えはホストの/etc/resolv.confです。デフォルト設定では、このファイルをそのままコピーを行っています。その場合、ホストにはsystemd-resolvedが動いていたため問題無かった設定を、systemd-resolvedが動いていないコンテナ上でも動作させてしまうことになります。

今回のトラブルはそのような原因で発生していました。

実際にこのトラブルを解決させる手法は以下の方法が挙げられます。

  • kube-dns の参照する上位のDNSサーバを変更する
    • (1) ConfigMapを用いて設定を行う
    • (2) kubeadmの設定を変更する
  • (3) /etc/resolv.conf のコピー元を指定する

ここでは(1)について説明します。下記のConfigMapファイルを用意します。

apiVersion: v1
kind: ConfigMap
metadata:
name: kube-dns
namespace: kube-system
data:
upstreamNameservers: |
["8.8.8.8", "8.8.4.4"]

上記のConfigMapを適用することでPod内のIPアドレスが適切に設定され、DNS名前解決をすることが可能となります。参考: kubernetes.io/docs

ちなみに出題したKubernetesのバージョンはv1.10.12で、現在のバージョンと鑑みても少し古いものでした。
このようなPull Requestによって、最近のkubeadmであれば今回のような状況に意図せずなってしまった場合でも警告が出るようになっているからという意図でした。

回答例

解説にもあった通り、下記のファイルを用意します。

$ cat kube-dns-conf.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-dns
namespace: kube-system
data:
upstreamNameservers: |
["8.8.8.8", "8.8.4.4"]

既にnamespaceなどの設定も書いてあるため、そのままapplyします。

$ kubectl apply -f kube-dns-conf.yaml

これにより設定が反映され、コンテナ内から名前解決が行えるようになりました。

採点基準

  • 名前解決が行えるか
  • 適切な設定が行われているか

この2点を確認していました。

実際にあった回答の中で減点した項目としては、名前解決が行えるものの永続化が行われていなかったり、上位サーバとして不適切なDNSサーバのIPアドレスが設定されていたりしており、それらの回答は減点を行いました。

まとめ

Kubernetesという比較的新しく大きなプロダクト上で出題した問題でしたが、実際に起きているのはDNSサーバの設定ミスというどのような環境であっても起こりえる問題でした。
回答に私用する設定もそれほど大きくなく、Kubernetesの公式ドキュメントにもあるため、なぜその現象が発生しているのかを把握できれば解決までは早かったのではないかと思います。

近年大きく注目を集めているDockerを始めとするコンテナ技術ですが、実際に動作しているのは慣れ親しんだ環境です。Kubernetesの使い方だけではなく、その中で動いているプロダクトや、それらのプロダクトがどうやって動作しているのか、管理側にも興味を持って頂ければと思います。


  1. https://netplan.io/ より引用